Analyse des AfD-Wahlerfolgs bei der Bundestagswahl 2021

1 Hintergrund

Die Bundestagswahl ist ein zentrales Ereignis für die Öffentlichkeit in Deutschland. Vor der hohen Relevanz dieses Ereignisses stellt sich die Frage, welche Faktoren die Ursache (oder zumindest Prädiktoren) der Wahlentscheidungen der Bürgerinnen und Bürger sind. Ist beispielsweise die Höhe der Arbeitslosigkeit in einem Wahlkreis ausschlaggebend, dass die eine oder andere Partei gewählt wird?

Die Analyse des Wahlerfolgs der Partei “Alternative für Deutschland” (AfD) ist von besonderem Interesse, da ein Teil ihrer Wählis und Vertretis offenbar die Grundsätze deutscher Politik hinterfragt und vielleicht entgegensteht. So hat das Bundesamt für Verfassungsschutz Teilorganisationen der AfD zum Verdachtsfall für Rechtsextremismus eingestuft.

Die Relevanz der Analyse extremistischer Bewegungen begründet sich mit einem Blick in die deutsche Geschichte: Die (deutsche) Geschichte des 20. Jahrhunderts zeigt, dass rechtsextreme Bewegungen das Potenzial für katastrophale Entwicklungen und schlimmste Verbrechen haben.

Ziel und Gegenstand dieses Workshops ist es nicht, eine Meinung oder ein Urteil über die Einschätzung des Verfassungsschutzes oder der AfD zu treffen. Vielmehr ist vor dem genannten Hintergrund die Analyse, warum bzw. unter welchen Randbedingungen die AfD Stimmen auf sich zieht, von höchstem gesellschaftlichem Interesse.

2 Hinweise

Ziel dieser Analyse ist es, grundlegende Methoden der Datenanalyse für eine angewandte Forschungsfrage bzw. eine Forschungsfrage von allgemeinem Interesse, vorzustellen bzw. einzuüben.

Es handelt sich um eine Analyse mit rein didaktischem Ziel.

Es sind keinerlei politische Aussagen mit dieser Analyse verbunden.

3 Forschungsfragen

  1. Wie groß ist der AfD-Stimmenanteil, aggregiert pro Bundesland und Deutschlandweit? Wie verteilt sich der Stimmenanteil der AfD?

  2. Wie hängt der Wahlerfolg der AfD mit ökonomopolitischen Indikatoren wie Arbeitslosigkeitsquote und Ausländeranteil zusammen?

4 Vorbereitung

4.1 R-Pakete

library(tidyverse)
library(sf)  # Geo-Visualisierung
library(rstatix)  # Deskriptive Statistiken
library(corrr)  # Korrelationsmatrizen
library(gt)  # HTML Tabellen
library(rstanarm)  # Bayes-Modellierung
library(tictoc)

5 Daten aufbereiten

5.1 Ökonomopolitische Strukturdaten

5.1.1 Daten einlesen

Die Strukturdaten sind vom Bundeswahlleiter zu beziehen; über diesen Link kommt man zu den Daten (CSV-Format).

Die Variablennamen sind hier erklärt.

d_str_file <- "data/btw21_Strukturdaten.csv"

d_str <- read_delim(d_str_file,
                    delim = ";", 
                    escape_double = FALSE,
                    locale = locale(decimal_mark = ",",
                                    grouping_mark = "."),
                    trim_ws = TRUE,
                    skip = 8) 

Hier sind die Namen der Spalten:

names(d_str)
##  [1] "Land"                                                                                                      
##  [2] "Wahlkreis-Nr."                                                                                             
##  [3] "Wahlkreis-Name"                                                                                            
##  [4] "Gemeinden am 31.12.2019 (Anzahl)"                                                                          
##  [5] "Fläche am 31.12.2019 (km²)"                                                                                
##  [6] "Bevölkerung am 31.12.2019 - Insgesamt (in 1000)"                                                           
##  [7] "Bevölkerung am 31.12.2019 - Deutsche (in 1000)"                                                            
##  [8] "Bevölkerung am 31.12.2019 - Ausländer/-innen (%)"                                                          
##  [9] "Bevölkerungsdichte am 31.12.2019 (EW je km²)"                                                              
## [10] "Zu- (+) bzw. Abnahme (-) der Bevölkerung 2019 - Geburtensaldo (je 1000 EW)"                                
## [11] "Zu- (+) bzw. Abnahme (-) der Bevölkerung 2019 - Wanderungssaldo (je 1000 EW)"                              
## [12] "Alter von ... bis ... Jahren am 31.12.2019 - unter 18 (%)"                                                 
## [13] "Alter von ... bis ... Jahren am 31.12.2019 - 18-24 (%)"                                                    
## [14] "Alter von ... bis ... Jahren am 31.12.2019 - 25-34 (%)"                                                    
## [15] "Alter von ... bis ... Jahren am 31.12.2019 - 35-59 (%)"                                                    
## [16] "Alter von ... bis ... Jahren am 31.12.2019 - 60-74 (%)"                                                    
## [17] "Alter von ... bis ... Jahren am 31.12.2019 - 75 und mehr (%)"                                              
## [18] "Bodenfläche nach Art der tatsächlichen Nutzung am 31.12.2019 - Siedlung und Verkehr (%)"                   
## [19] "Bodenfläche nach Art der tatsächlichen Nutzung am 31.12.2019 - Vegetation und Gewässer (%)"                
## [20] "Fertiggestellte Wohnungen 2019 (je 1000 EW)"                                                               
## [21] "Bestand an Wohnungen am 31.12.2019 - insgesamt (je 1000 EW)"                                               
## [22] "Wohnfläche am 31.12.2019 (je Wohnung)"                                                                     
## [23] "Wohnfläche am 31.12.2019 (je EW)"                                                                          
## [24] "PKW-Bestand am 01.01.2020 - PKW insgesamt (je 1000 EW)"                                                    
## [25] "PKW-Bestand am 01.01.2020 - PKW mit Elektro- oder Hybrid-Antrieb (%)"                                      
## [26] "Unternehmensregister 2018 - Unternehmen insgesamt (je 1000 EW)"                                            
## [27] "Unternehmensregister 2018 - Handwerksunternehmen (je 1000 EW)"                                             
## [28] "Schulabgänger/-innen beruflicher Schulen 2019"                                                             
## [29] "Schulabgänger/-innen allgemeinbildender Schulen 2019 - insgesamt ohne Externe (je 1000 EW)"                
## [30] "Schulabgänger/-innen allgemeinbildender Schulen 2019 - ohne Hauptschulabschluss (%)"                       
## [31] "Schulabgänger/-innen allgemeinbildender Schulen 2019 - mit Hauptschulabschluss (%)"                        
## [32] "Schulabgänger/-innen allgemeinbildender Schulen 2019 - mit mittlerem Schulabschluss (%)"                   
## [33] "Schulabgänger/-innen allgemeinblldender Schulen 2019 - mit allgemeiner und Fachhochschulreife (%)"         
## [34] "Kindertagesbetreuung am 01.03.2020 - Betreute Kinder unter 3 Jahre (Betreuungsquote)"                      
## [35] "Kindertagesbetreuung am 01.03.2020 - Betreute Kinder 3 bis unter 6 Jahre (Betreuungsquote)"                
## [36] "Verfügbares Einkommen der privaten Haushalte 2018 (EUR je EW)"                                             
## [37] "Bruttoinlandsprodukt 2018 (EUR je EW)"                                                                     
## [38] "Sozialversicherungspflichtig Beschäftigte am 30.06.2020 - insgesamt (je 1000 EW)"                          
## [39] "Sozialversicherungspflichtig Beschäftigte am 30.06.2020 - Land- und Forstwirtschaft, Fischerei (%)"        
## [40] "Sozialversicherungspflichtig Beschäftigte am 30.06.2020 - Produzierendes Gewerbe (%)"                      
## [41] "Sozialversicherungspflichtig Beschäftigte am 30.06.2020 - Handel, Gastgewerbe, Verkehr (%)"                
## [42] "Sozialversicherungspflichtig Beschäftigte am 30.06.2020 - Öffentliche und private Dienstleister (%)"       
## [43] "Sozialversicherungspflichtig Beschäftigte am 30.06.2020 - Übrige Dienstleister und \"\"ohne Angabe\"\" (%)"
## [44] "Empfänger/-innen von Leistungen nach SGB II  Oktober 2020 -  insgesamt (je 1000 EW)"                       
## [45] "Empfänger/-innen von Leistungen nach SGB II  Oktober 2020 -  nicht erwerbsfähige Hilfebedürftige (%)"      
## [46] "Empfänger/-innen von Leistungen nach SGB II  Oktober 2020 -  Ausländer/-innen (%)"                         
## [47] "Arbeitslosenquote Februar 2021 - insgesamt"                                                                
## [48] "Arbeitslosenquote Februar 2021 - Männer"                                                                   
## [49] "Arbeitslosenquote Februar 2021 - Frauen"                                                                   
## [50] "Arbeitslosenquote Februar 2021 - 15 bis 24 Jahre"                                                          
## [51] "Arbeitslosenquote Februar 2021 - 55 bis 64 Jahre"                                                          
## [52] "Fußnoten"

Es ist vielleicht praktisch, die Spaltennamen für spätere Verwendung in einer Textdatei abzuspeichern:

d_str_names <-
  tibble(
    var_name = names(d_str),
    ) %>% 
  mutate(id = row_number())

write_csv(d_str_names, "objects/d_str_names.csv")

5.1.2 Daten aufbereiten

Die Spaltennamen sind etwas unhandlich. Formulieren wir lieber prägnanter:

names(d_str) <- paste0("V",1:ncol(d_str))

d_str2 <-
  d_str %>% 
  select(state = V1,
         area_nr = V2,
         area_name = V3,
         for_prop = V8,
         pop_density = V9,
         pop_move = V11,
         income = V36,
         unemp = V47) 

Sichern wir diese Daten in eine Datei:

write_csv(d_str2, file = "objects/d_str2.csv")

5.2 Wahlergebnisse

5.2.1 Daten einlesen

Die Daten sind vom Bundeswahlleiter zu beziehen. Unter diesem Link kommt man direkt zur CSV-Datei.

Eine Erklärung zu den Variablen findet sich hier.

elec_results_file <- "https://www.bundeswahlleiter.de/bundestagswahlen/2021/ergebnisse/opendata/csv/kerg2.csv"

elec_results <- read_delim(elec_results_file,
                    delim = ";", 
                    escape_double = FALSE,
                    locale = locale(decimal_mark = ",",
                                    grouping_mark = "."),
                    trim_ws = TRUE,
                    skip = 9
                    ) 

5.2.2 Daten aufbereiten

Konzentrieren wir uns auf die Zweitstimme, da die bei der BTW die entscheidende ist.

elec_results2 <- 
  elec_results %>% 
  select(Gebietsart, Gebietsnummer, Gebietsname, UegGebietsart, UegGebietsnummer, Gruppenart, Gruppenname, Stimme, Prozent, DiffProzentPkt) %>% 
  filter(Gruppenname == "AfD") %>% 
  filter(Stimme == 2)
elec_results2 <- 
  elec_results2 %>% 
  mutate(Gebietsname = ifelse(Gebietsname == "Bundesgebiet",
                              "Deutschland",
                              Gebietsname))

Sichern wir diese Daten in eine Datei:

write_csv(elec_results2, file = "objects/elec_results2.csv")

6 Einfache, univariate Ergebnisse

6.1 Mittelwert des AfD-Wahlerfolgs

6.1.1 … über alle Bundesländer

Achtung! Alle Bundesländer werden gleich gewichtet!

elec_results2 %>% 
  filter(Gebietsart == "Land") %>% 
  summarise(AfD_mean = mean(Prozent))
## # A tibble: 1 × 1
##   AfD_mean
##      <dbl>
## 1     12.1

6.1.2 … über Deutschland

elec_results2 %>% 
  filter(Gebietsart == "Bund") %>% 
  summarise(AfD_mean = mean(Prozent)) 
## # A tibble: 1 × 1
##   AfD_mean
##      <dbl>
## 1     10.3

6.1.3 … über Wahlkreise

elec_results2 %>% 
  filter(Gebietsart == "Wahlkreis") %>% 
  summarise(AfD_mean = mean(Prozent, na.rm = T))
## # A tibble: 1 × 1
##   AfD_mean
##      <dbl>
## 1     10.5

6.2 Wahlerfolg der AfD nach Bundesländern

elec_results2 %>%
  filter(Gebietsart == "Land") %>% 
  select(Gebietsname, Prozent) %>% 
  mutate(Gebietsname = as.factor(Gebietsname)) %>% 
  ggplot(aes(x =  reorder(Gebietsname, Prozent), 
             y = Prozent)) +
  geom_col() +
  coord_flip() +
  labs(title = "AfD-Zweitstimmenanteil bei der BTW 21",
       caption = "Die Linie zeigt den Mittelwert für ganz Deutschland",
       y = "Anteil in Prozent",
       x = "Bundesländer") +
  geom_hline(yintercept = 10.1) +
  annotate("label", x = "Hamburg", y = 10,
           label = "Mittelwert",
           size = 2) +
  geom_text(aes(label = round(Prozent)),
            nudge_y = -1,
            color = "white",
            size = 2)

7 Daten zusammenführen (join)

7.1 Erster Versuch

d <- 
  d_str2 %>% 
  full_join(elec_results2, by = c("area_name" = "Gebietsname"))

Einige Zeilen lassen sich nicht zusammenführen. Schauen wir diese uns näher an:

d %>% 
  filter(str_detect(area_name, "nsgesamt"))  # Ohne "I"!
## # A tibble: 17 × 17
##    state area_nr area_name for_prop pop_density pop_move income unemp Gebietsart
##    <chr> <chr>   <chr>        <dbl>       <dbl>    <dbl>  <dbl> <dbl> <chr>     
##  1 Schl… 901     Land ins…      8.4        184.      6    22833   6.3 <NA>      
##  2 Meck… 913     Land ins…      4.7         69       5    19470   8.7 <NA>      
##  3 Hamb… 902     Land ins…     16.5       2446.      2.7  25029   8.1 <NA>      
##  4 Nied… 903     Land ins…      9.7        168.      4.4  21988   6.1 <NA>      
##  5 Brem… 904     Land ins…     18.5       1624.     -1.1  21481  11.6 <NA>      
##  6 Bran… 912     Land ins…      5           85       9.1  20475   6.6 <NA>      
##  7 Sach… 915     Land ins…      5.1        107.      1.1  19528   8.3 <NA>      
##  8 Berl… 911     Land ins…     19.2       4118.      6.3  20972  10.6 <NA>      
##  9 Nord… 905     Land ins…     13.6        526.      2.6  22294   7.9 <NA>      
## 10 Sach… 914     Land ins…      5.1        221.      3.8  20335   6.6 <NA>      
## 11 Hess… 906     Land ins…     16.6        298.      4.5  23943   5.7 <NA>      
## 12 Thür… 916     Land ins…      5.2        132.      1.6  19793   6.4 <NA>      
## 13 Rhei… 907     Land ins…     11.5        206.      5    23197   5.6 <NA>      
## 14 Baye… 909     Land ins…     13.6        186.      4.5  25309   4.2 <NA>      
## 15 Bade… 908     Land ins…     15.9        310.      3.4  24892   4.4 <NA>      
## 16 Saar… 910     Land ins…     11.4        384.      2.2  20277   7.4 <NA>      
## 17 Deut… 999     Insgesamt     12.5        233.      3.9  22899   6.3 <NA>      
## # … with 8 more variables: Gebietsnummer <chr>, UegGebietsart <chr>,
## #   UegGebietsnummer <chr>, Gruppenart <chr>, Gruppenname <chr>, Stimme <dbl>,
## #   Prozent <dbl>, DiffProzentPkt <dbl>

Es sind die Bundesländer, deren area_name “Land insgesamt”, jeweils, lautet.

Diese Felder müssen wir wohl umbenennen.

Bundesländer - plus der Bund als Ganzes - haben eine ID, die mit 9 beginnt, was für sonstige Einheiten nicht der Fall ist:

d_leander <- 
d %>% 
  select(area_nr, area_name, state) %>% 
  filter(str_detect(area_nr, "^9")) %>% 
  mutate(area_name = state) %>% 
  select(-state)

Da die Länder auch noch noch eine andere Gebietsnummer haben in der Datei mit den Strukturdaten, fügen wir noch die Gebietsnummer aus elec_results2 hinzu:

d_laender2 <-
  d_leander %>% 
  left_join(elec_results2 %>% select(Gebietsname, Gebietsnummer), by = c("area_name" = "Gebietsname"))

Sieht dann so aus:

d_laender2
## # A tibble: 17 × 3
##    area_nr area_name              Gebietsnummer
##    <chr>   <chr>                  <chr>        
##  1 901     Schleswig-Holstein     01           
##  2 913     Mecklenburg-Vorpommern 13           
##  3 902     Hamburg                02           
##  4 903     Niedersachsen          03           
##  5 904     Bremen                 04           
##  6 912     Brandenburg            12           
##  7 915     Sachsen-Anhalt         15           
##  8 911     Berlin                 11           
##  9 905     Nordrhein-Westfalen    05           
## 10 914     Sachsen                14           
## 11 906     Hessen                 06           
## 12 916     Thüringen              16           
## 13 907     Rheinland-Pfalz        07           
## 14 909     Bayern                 09           
## 15 908     Baden-Württemberg      08           
## 16 910     Saarland               10           
## 17 999     Deutschland            99

7.2 Zweiter Versuch

Jetzt fügen wir die korrigierten Landesnamen und -nummern zu d_str2 hinzu:

d_str3 <-
  d_str2 %>% 
  left_join(d_laender2, by = "area_nr")

area_name.y ist aktuell nur mit Landesnamen (plus Bund) gefüllt. Ergänzen wir also die NAs mit den Namen der Wahlbezirke. Die andere Spalte area_name.x brauchen wir dann nicht mehr.

d_str4 <- 
  d_str3 %>% 
  mutate(area_name.y = ifelse(is.na(area_name.y),
                              area_name.x,
                              area_name.y)) %>% 
  select(-area_name.x) %>% 
  rename(area_name = area_name.y)

Das Gleiche machen wir mit area_nr:

d_str5 <-
  d_str4 %>% 
  mutate(area_nr = ifelse(is.na(Gebietsnummer),
                                area_nr,
                                Gebietsnummer))
d_str5 %>% 
  slice_head(n=20) 
## # A tibble: 20 × 9
##    state    area_nr for_prop pop_density pop_move income unemp area_name        
##    <chr>    <chr>      <dbl>       <dbl>    <dbl>  <dbl> <dbl> <chr>            
##  1 Schlesw… 001          8.4       137.       9.5  21358   7   Flensburg – Schl…
##  2 Schlesw… 002          7.1        84.6      8.3  24354   6.5 Nordfriesland – …
##  3 Schlesw… 003          6.5       110.       4.6  22292   6.4 Steinburg – Dith…
##  4 Schlesw… 004          5.5       116.       8.6  23410   4.8 Rendsburg-Eckern…
##  5 Schlesw… 005         11.4      1879.      -1.8  19718   8.4 Kiel             
##  6 Schlesw… 006          8.7       171.       5.1  22081   6.7 Plön – Neumünster
##  7 Schlesw… 007         11.1       476.       8.1  24708   5.9 Pinneberg        
##  8 Schlesw… 008          8         240.       5.7  23952   5   Segeberg – Storm…
##  9 Schlesw… 009          5.7       144.       6.8  23411   6.1 Ostholstein – St…
## 10 Schlesw… 010          8.6       237.       8.8  24571   5.1 Herzogtum Lauenb…
## 11 Schlesw… 011         10         580        1.3  20404   8.5 Lübeck           
## 12 Schlesw… 01           8.4       184.       6    22833   6.3 Schleswig-Holste…
## 13 Mecklen… 012          6.7        73.9      2.6  19927   8   Schwerin – Ludwi…
## 14 Mecklen… 013          4          63.3      7.5  20014   6.8 Ludwigslust-Parc…
## 15 Mecklen… 014          5.5       278        6    19274   7.9 Rostock – Landkr…
## 16 Mecklen… 015          4.7        84.7      6.7  19231  11   Vorpommern-Rügen…
## 17 Mecklen… 016          3.8        53.8      2.8  18774   9.9 Mecklenburgische…
## 18 Mecklen… 017          3          40        4.2  19679   8.5 Mecklenburgische…
## 19 Mecklen… 13           4.7        69        5    19470   8.7 Mecklenburg-Vorp…
## 20 Hamburg  018         21.8      3044.       2.7  25029   8.1 Hamburg-Mitte    
## # … with 1 more variable: Gebietsnummer <chr>

Scheint zu passen.

d <-
  d_str5 %>% 
  left_join(elec_results2, by = c("area_nr" = "Gebietsnummer"))
dim(d)
## [1] 316  18

316 ist eine gute Zahl:

  • 299 Wahlbezirke
  • 16 Länder
  • 1 Bund

7.3 Check

Prüfen wir, ob es noch fehlende Werte nach dem Join gibt:

d %>% 
  filter(is.na(Gruppenname), is.na(Prozent)) %>% 
  select(area_name, area_nr, Prozent) %>% 
  nrow()
## [1] 0

Zählen wir die Anzahl der Wahleinheiten nach Art:

d %>% 
  group_by(Gebietsart) %>% 
  count()
## # A tibble: 3 × 2
## # Groups:   Gebietsart [3]
##   Gebietsart     n
##   <chr>      <int>
## 1 Bund           1
## 2 Land          16
## 3 Wahlkreis    299

Das sieht gut aus.

7.4 Geo-Daten

Die Geodaten sind ebenfalls erhältlich beim Bundeswahlleiter.

Die Geodaten zur Visualisierung der Wahlkreise werden im sog. “Shape-Format” (.shp) geliefert.

geo_file <- "data/btw21_geometrie_wahlkreise_shp/Geometrie_Wahlkreise_20DBT.shp"

Einlesen:

wahlkreise_shp <- st_read(geo_file)
## Reading layer `Geometrie_Wahlkreise_20DBT' from data source 
##   `/Users/sebastiansaueruser/github-repos/afd-btw21-analyse/data/btw21_geometrie_wahlkreise_shp/Geometrie_Wahlkreise_20DBT.shp' 
##   using driver `ESRI Shapefile'
## Simple feature collection with 299 features and 4 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: 280371.1 ymin: 5235856 xmax: 921120.1 ymax: 6101444
## Projected CRS: ETRS89 / UTM zone 32N

Hilfe zu st_read() findet sich hier oder auf der Dokumentation zum R-Paket sf (simple feature).

Plotten:

wahlkreise_shp %>%
  ggplot() +
  geom_sf()

Der Aufbau des Datensatzes ist aufgeräumt:

glimpse(wahlkreise_shp)
## Rows: 299
## Columns: 5
## $ WKR_NR    <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 1…
## $ WKR_NAME  <chr> "Flensburg – Schleswig", "Nordfriesland – Dithmarschen Nord"…
## $ LAND_NR   <chr> "01", "01", "01", "01", "01", "01", "01", "01", "01", "01", …
## $ LAND_NAME <chr> "Schleswig-Holstein", "Schleswig-Holstein", "Schleswig-Holst…
## $ geometry  <MULTIPOLYGON [m]> MULTIPOLYGON (((545529.8 60..., MULTIPOLYGON ((…

7.5 Geo-Daten joinen

d2 <-
  d %>% 
  mutate(area_nr = as.integer(area_nr)) %>% 
  full_join(wahlkreise_shp, by = c("area_nr" = "WKR_NR"))

8 Geo-Vis

8.1 AfD-Anteil

d2 %>% 
  ggplot() +
  geom_sf(aes(fill = Prozent,
              geometry = geometry))

Verschönern:

d2 %>% 
  ggplot() +
  geom_sf(aes(fill = Prozent,
              geometry = geometry),
          color = NA) +
  scale_fill_viridis_c() +
  theme_void() +
  labs(title = "Anteil der Zweitstimmen für die AfD",
       subtitle = "Bundestagswahl 2021",
        fill = "Anteil AfD-Zweitstimmen") +
  theme(legend.position = "bottom")

Ein paar Wahlkreise haben entweder nicht erwischt, oder es gibt keine Daten.

8.2 Ausländeranteil

d2 %>% 
  ggplot() +
  geom_sf(aes(fill = for_prop,
              geometry = geometry),
          color = NA) +
  scale_fill_viridis_c() +
  theme_void()

8.3 Arbeitslosigkeit

d2 %>% 
  ggplot() +
  geom_sf(aes(fill = unemp,
              geometry = geometry),
          color = NA) +
  scale_fill_viridis_c() +
  theme_void()

8.4 Bevölkerungsdichte

d2 %>% 
  ggplot() +
  geom_sf(aes(fill = pop_density,
              geometry = geometry),
          color = NA) +
  scale_fill_viridis_c() +
  theme_void()

Oder vielleicht lieber Bevölkerungsdichte in der Log2-Skala?

d2 %>% 
  ggplot() +
  geom_sf(aes(fill = log2(pop_density),
              geometry = geometry),
          color = NA) +
  scale_fill_viridis_c() +
  theme_void()

So kommen die Unterschiede optisch deutlich besser zum Tragen.

Zur Erinnerung: +1 auf der Log2-Skala entspricht einer Multiplikation mit 2 auf der Rohskala. Die Log2-Skala zählt also “Verdopplungsschritte”.

9 EDA

9.1 Prädiktoren des AfD-Wahlerfolgs

9.1.1 Ausländeranteil

d2 %>% 
  filter(Gebietsart == "Wahlkreis") %>% 
  select(Prozent, for_prop) %>% 
  ggplot() +
  aes(x = for_prop, y = Prozent) +
  geom_point(alpha = .7) +
  geom_smooth()

Interessant! Ein einfacher linearer Trend liegt nicht vor. Vielleicht sehen wir eher zwei, unterschiedliche Cluster?

Cluster 1 ist geprägt von hohem Ausländeranteil und Cluster 2 von geringem. In beiden Clustern ist der Zusammenhang negativ. Allerdings ist dieser Zusammenhang deutlich stärker ausgeprägt für Cluster 1.

9.1.2 Ausländeranteil nach Bundesland

Wenn wir diese Analyse aufteilen nach Bundesländern, sehen wir viellicht klarer.

d2 %>% 
  filter(Gebietsart == "Wahlkreis") %>% 
  select(Prozent, for_prop, state) %>% 
  ggplot() +
  aes(x = for_prop, y = Prozent, color = state) +
  geom_point(alpha = .7) +
  geom_smooth(method = "lm") +
  facet_wrap(~ state) +
  scale_y_continuous(limits = c(0, 40)) +
  theme(legend.position = "none")

Es scheint sich in jedem Bundesland ein negativer, linearer Zusammenhang zu zeigen. Es sei denn, der AfD-Anteil ist sehr gering, dann wird der Zusammenhang schwach, also eine Bodeneffekt.

9.2 Bivariate Korrelationen

d2 %>% 
  select(Prozent, for_prop, unemp, income, pop_density) %>% 
  cor_mat()
## # A tibble: 5 × 6
##   rowname     Prozent for_prop  unemp income pop_density
## * <chr>         <dbl>    <dbl>  <dbl>  <dbl>       <dbl>
## 1 Prozent       1        -0.58  0.038 -0.44       -0.3  
## 2 for_prop     -0.58      1     0.23   0.38        0.65 
## 3 unemp         0.038     0.23  1     -0.56        0.51 
## 4 income       -0.44      0.38 -0.56   1           0.046
## 5 pop_density  -0.3       0.65  0.51   0.046       1
d2 %>% 
  select(Prozent, for_prop, unemp, income, pop_density) %>% 
  cor_mat() %>% 
  cor_plot()

Das Kreuz zeigt wohl eine Korrelation nahe Null an.

Die Korrelationsmatrix im langen Format:

d2 %>% 
  select(Prozent, for_prop, unemp, income, pop_density) %>% 
  cor_mat() %>% 
  cor_gather() %>% 
  filter(cor != 1) %>% 
  filter(var1 == "Prozent") %>% 
  arrange(cor) %>% 
  gt() %>% 
  fmt_number(where(is.numeric), decimals = 2)
var1 var2 cor p
Prozent for_prop −0.58 0.00
Prozent income −0.44 0.00
Prozent pop_density −0.30 0.00
Prozent unemp 0.04 0.50

Je mehr Ausländer, oder auch je mehr Einkommen, desto weniger wird AfD gewählt. Die Arbeitslosigkeit steht fast nicht in einem (linearen) Zusammenhang mit dem AfD-Wahlerfolg.

10 Modellierung

10.1 Daten aufbereiten

Alle Prozessorkerne nutzen:

options(mc.cores = parallel::detectCores())

Daten aufbereiten:

d3 <-
  d2 %>% 
  mutate(Prozent_z = scale(Prozent),
         for_prop_z = scale(for_prop),
         unemp_z = scale(unemp)) %>% 
  select(Prozent_z, state, for_prop_z, unemp_z, Gebietsname) %>% 
  filter(state != "Deutschland")

10.2 Model 1

10.2.1 Modell berechnen

Modell berechnen:

tic()
m1 <- 
  stan_glm(Prozent_z ~ state + unemp_z + for_prop_z,
               data = d3,
           refresh = 0
           )
toc()
## 2.435 sec elapsed

Modellergebnisse:

m1
## stan_glm
##  family:       gaussian [identity]
##  formula:      Prozent_z ~ state + unemp_z + for_prop_z
##  observations: 315
##  predictors:   18
## ------
##                             Median MAD_SD
## (Intercept)                  0.3    0.1  
## stateBayern                 -0.2    0.1  
## stateBerlin                 -0.6    0.2  
## stateBrandenburg             0.6    0.2  
## stateBremen                 -1.0    0.3  
## stateHamburg                -1.1    0.2  
## stateHessen                 -0.2    0.1  
## stateMecklenburg-Vorpommern  0.3    0.2  
## stateNiedersachsen          -0.9    0.1  
## stateNordrhein-Westfalen    -0.9    0.1  
## stateRheinland-Pfalz        -0.4    0.1  
## stateSaarland               -0.5    0.2  
## stateSachsen                 1.7    0.2  
## stateSachsen-Anhalt          0.7    0.2  
## stateSchleswig-Holstein     -1.1    0.2  
## stateThüringen               1.6    0.2  
## unemp_z                      0.2    0.0  
## for_prop_z                  -0.3    0.0  
## 
## Auxiliary parameter(s):
##       Median MAD_SD
## sigma 0.4    0.0   
## 
## ------
## * For help interpreting the printed output see ?print.stanreg
## * For info on the priors used see ?prior_summary.stanreg

Modellparameter visualisieren:

plot(m1)

10.2.2 Modellergebnisse interpretieren

Fast alle Modellparameter werden vom Modell als relevant angesehen - mit Ausnahme des Koeffizienten für Mecklenburg-Vorpommern.

Die stärksten Koeffizienten finden wir für Sachsen und Thüringen. Das sind auch die Länder mit dem höchsten Wahlerfolg für die AfD.